package com.yfbao.horizon.schedule.tk.log;

import org.osgi.service.log.LogEntry;

import java.io.IOException;
import java.io.RandomAccessFile;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.HashMap;
import java.util.Map;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class MappedFileWriter {
    private static final int MAPPED_SIZE = 256 * 1024 * 1024; // 256MB 映射窗口
    private static final long MAX_FILE_SIZE_BYTES = 50L * 1024 * 1024; // 50MB 单文件滚动阈值
    private static final String LOG_DIR = "logs";
    private static final String ACTIVE_FILE = LOG_DIR + "/frontend.log";
    private static final String ROLLING_PATTERN = LOG_DIR + "/frontend.%s.%d.log"; // date + index
    private static final int MAX_HISTORY_PER_DATE = 3;

    private static MappedByteBuffer currentBuffer;
    private static FileChannel currentChannel;
    private static RandomAccessFile raf;
    private static long currentPosition;

    // rolling state
    private static String currentDate = dateStr();
    private static int fileIndex = 0;

    public static void writeBatch(List<LogEntry> batch) throws IOException {
        // 日期切换检测（按天滚动）
        String today = dateStr();
        if (!today.equals(currentDate)) {
            log.info("Date changed ({} -> {}), rotate file", currentDate, today);
            currentDate = today;
            fileIndex = 0;
            rotateFile(true); // force roll current active file to previous date
        }

        for (LogEntry entry : batch) {
            String logLine = JSONUtil.toJsonStr(serialize(entry)) + "\n";
            byte[] bytes = logLine.getBytes(StandardCharsets.UTF_8);

            // 检查剩余空间（达到上限则滚动）
            if (currentBuffer == null || currentPosition + bytes.length > MAX_FILE_SIZE_BYTES) {
                rotateFile(false);
            }

            currentBuffer.put(bytes);
            currentPosition += bytes.length;
        }
        // 批处理完成日志（仅用于运维观察，可按需调整级别或去除）
        log.debug("batch success, size={}", batch.size());
    }

    private static Map<String, Object> serialize(LogEntry e) {
        Map<String, Object> m = new HashMap<>();
        try { m.put("time", e.getTime()); } catch (Throwable ignored) {}
        try { m.put("level", e.getLevel()); } catch (Throwable ignored) {}
        try { m.put("logLevel", e.getLogLevel() != null ? e.getLogLevel().name() : null); } catch (Throwable ignored) {}
        try { m.put("loggerName", e.getLoggerName()); } catch (Throwable ignored) {}
        try { m.put("message", e.getMessage()); } catch (Throwable ignored) {}
        try { Throwable ex = e.getException(); m.put("exception", ex != null ? ex.toString() : null); } catch (Throwable ignored) {}
        try { m.put("thread", e.getThreadInfo()); } catch (Throwable ignored) {}
        try { m.put("sequence", e.getSequence()); } catch (Throwable ignored) {}
        try {
            StackTraceElement loc = e.getLocation();
            if (loc != null) {
                Map<String, Object> lm = new HashMap<>();
                lm.put("class", loc.getClassName());
                lm.put("method", loc.getMethodName());
                lm.put("file", loc.getFileName());
                lm.put("line", loc.getLineNumber());
                m.put("location", lm);
            }
        } catch (Throwable ignored) {}
        return m;
    }

    private static void rotateFile(boolean forceRollActive) throws IOException {
        // 关闭现有映射与通道
        if (currentBuffer != null) {
            try { currentBuffer.force(); } catch (Throwable ignored) {}
        }
        if (currentChannel != null) {
            try { currentChannel.close(); } catch (Throwable ignored) {}
            currentChannel = null;
        }
        if (raf != null) {
            try { raf.close(); } catch (Throwable ignored) {}
            raf = null;
        }

        // 确保日志目录存在
        Files.createDirectories(Paths.get(LOG_DIR));

        File active = new File(ACTIVE_FILE);
        // 将当前活跃文件滚动为带日期与序号的文件
        if ((forceRollActive || currentPosition > 0) && active.exists()) {
            String rolled = String.format(ROLLING_PATTERN, currentDate, fileIndex++);
            boolean ok = active.renameTo(new File(rolled));
            if (ok) {
                log.info("Rolled log file to {}", rolled);
                cleanupHistory(currentDate);
            } else {
                log.warn("Failed to roll log file {} -> {}", ACTIVE_FILE, rolled);
            }
        }

        // 打开新活跃文件并映射
        raf = new RandomAccessFile(ACTIVE_FILE, "rw");
        // 确保文件大小足以映射
        if (raf.length() < MAPPED_SIZE) {
            raf.setLength(MAPPED_SIZE);
        }
        currentChannel = raf.getChannel();
        currentBuffer = currentChannel.map(FileChannel.MapMode.READ_WRITE, 0, MAPPED_SIZE);
        currentPosition = 0;
    }

    private static void cleanupHistory(String date) {
        File dir = new File(LOG_DIR);
        File[] files = dir.listFiles((d, name) -> name.startsWith("frontend." + date + ".") && name.endsWith(".log"));
        if (files == null || files.length <= MAX_HISTORY_PER_DATE) return;
        Arrays.sort(files, Comparator.comparing(File::getName));
        int toDelete = files.length - MAX_HISTORY_PER_DATE;
        for (int i = 0; i < toDelete; i++) {
            if (files[i].delete()) {
                log.debug("Deleted old rolled log {}", files[i].getName());
            }
        }
    }

    private static String dateStr() {
        return new SimpleDateFormat("yyyy-MM-dd").format(new Date());
    }

    public static void main(String[] args) {

    }
}